package com.bpvse;


import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import com.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

//搜索的获得的是EI的结果，也就是str,这个结果是index(索引的加密结果)，因此要获得原始的索引值，需要进行原始值的解密

@Service
public class SearchService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    private static final byte[] strEOF = new byte[]{69, 79, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
//
//    public byte[] strEOF = {
//            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
//            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
//            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
//            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff
//    };
    public int[] search(int resultLen, int astLen, byte[] trapdoor) throws Exception{
        byte[] IH = new byte[DataUnit.HASH_LEN];
        byte[] st2 = trapdoor;  //最新版本对应的状态
        byte[] u = new byte[DataUnit.HASH_LEN];
        byte[] Pv2 = new byte[DataUnit.HASH_LEN];
        byte[] Pv2T = new byte[DataUnit.HASH_LEN];
        byte[] Pv1 = new byte[DataUnit.HASH_LEN];

        byte[] Sa = new byte[DataUnit.HASH_LEN];
        byte[] Sv = new byte[DataUnit.HASH_LEN + 4];
        byte[] tmp1 = new byte[DataUnit.HASH_LEN + 4];

        byte[] sDB = new byte[4];
        int iDB;
        byte[] st1 = new byte[DataUnit.HASH_LEN];

        byte[] Ia = new byte[DataUnit.HASH_LEN];
        byte[] Iv = new byte[DataUnit.HASH_LEN + 4];

        byte[] EI = new byte[DataUnit.HASH_LEN];
        byte[] hd = new byte[DataUnit.HASH_LEN];
        int[] result = new int[3];

        int version = -1;
        resultLen = 0;


        //最新版本对应的状态
        System.out.println("搜索时对应的st2:"+ Base64.getEncoder().encodeToString(st2));
        System.out.println("st2的内容是:"+Arrays.toString(st2));
        //u就是获得pa2,通过pa1找到其证明pv2
        hUnit.h(u, st2, DataUnit.HASH_LEN, (byte) 0);
        System.out.println("pa2的加密内容是:" + Base64.getEncoder().encodeToString(u));


        //reply就是获得pv2，获得它对应的证明
        String reply = stringRedisTemplate.opsForValue().get(Base64.getEncoder().encodeToString(u));
        System.out.println("pv2的加密内容是:" + reply);
        if (reply != null) {
            //将字符串reply转换成字符数组byte[]Pv2
            Pv2 = Base64.getDecoder().decode(reply);
            version = 0;
            astLen = 0;

            //当最新状态st2(陷门)不是空时
            while (!strequalUnit.strequal(st2,  strEOF, DataUnit.HASH_LEN)) {
                //获得最新版本对应的状态st2的加密Sa
                hUnit.h(Sa, st2, DataUnit.HASH_LEN, (byte) 1);

                //通过加密的Sa获得其sv
                reply = stringRedisTemplate.opsForValue().get(Base64.getEncoder().encodeToString(Sa));
                Sv = Base64.getDecoder().decode(reply);

                h2Unit.h2(tmp1, st2, DataUnit.HASH_LEN);
                strXorUnit.strXor(tmp1, Sv, tmp1, DataUnit.HASH_LEN + 4);
                System.arraycopy(tmp1, 0, sDB, 0, 4);
                System.out.println("sDB的内容"+ Arrays.toString(sDB));
                System.out.println("sDB的加密内容"+bytesToHexUnit.byteTo(sDB));
                //获得最新版本前一版本st1的内容
                System.arraycopy(tmp1, 4, st1, 0, DataUnit.HASH_LEN);
                System.out.println("st1的内容是"+Arrays.toString(st1));
                iDB = Char2intUnit.char2int(sDB, 4);

                //
                System.out.println("iDB的是"+iDB);

                resetUnit.reset(IH, DataUnit.HASH_LEN);
                for (int i = 0; i < iDB; i++) {

                    //获得对应的文件索引Ia
                    h34Unit.h34(Ia, st2, DataUnit.HASH_LEN, i, 3);
                    System.out.println("Ia的加密内容是:" +Base64.getEncoder().encodeToString(Ia));
                    //获得文件索引值IV
                    reply = stringRedisTemplate.opsForValue().get(Base64.getEncoder().encodeToString(Ia));
                    System.out.println("Iv的加密内容是:" + reply);
                    EI = Base64.getDecoder().decode(reply);   //IV = EI
                    System.out.println("EI的加密内容是:" +Base64.getEncoder().encodeToString(EI));

                    h34Unit.h34(Iv, st2, DataUnit.HASH_LEN, i, 4);
                    System.out.println("第二次计算获得Iv的值"+Arrays.toString(Iv));

                    //异或得到EI
                    strXorUnit.strXor(EI, Iv, EI, DataUnit.HASH_LEN);   //EI就是返回结果
                    System.out.println("EI的结果是:"+Arrays.toString(EI));
                    addHashUnit.addHash(IH, IH, EI, DataUnit.HASH_LEN);
                    String strV = Base64.getEncoder().encodeToString(EI);  //strV记录返回结果,将字符数组转化成字符串类型（EI->strV）
                    System.out.println("strv的字符串结果是:"+strV);
                    System.out.println("strV的结果是:"+Arrays.toString(Base64.getDecoder().decode(strV)));  //将原来的字符串转化成字符数组,并且将其内容打印出来
                    //获得最后的结果，并且存放在map里面(从后往前存储)
                    StoreUnit.TR.put(resultLen, strV);  //(0,st2的结果)
                    resultLen++;
                    System.out.println("resultLen的值:"+resultLen);
                }
                System.arraycopy(IH, 0, hd, 0, DataUnit.HASH_LEN);

                //通过st1找到前一版本的pa1
                hUnit.h(u, st1, DataUnit.HASH_LEN, (byte)0);
                System.out.println("Pa1的内容是多少" + Arrays.toString(u));
                System.out.println("Pa1的加密内容是:" + Base64.getEncoder().encodeToString(u));
                //通过pa1找到前一版本的pv1
                reply = stringRedisTemplate.opsForValue().get(Base64.getEncoder().encodeToString(u));
                System.out.println("pv1的值是:"+reply);
                if (reply != null) {
                    Pv1 = Base64.getDecoder().decode(reply);
                } else {
                    //没有找到就是前一个状态错误
                    System.out.println("Recover error-1!");
                    return null;
                }

                //通过pv1和hd来获得pv2T
                hkeyUnit.hkey(Pv2T, hd, Pv1, DataUnit.HASH_LEN, st2, DataUnit.HASH_LEN);  //对应的状态和它自己的pv结合起来生成的就是最新的pv(每次计算前pV2T的值应该为0)
                System.out.println("Pv2T的加密内容是:" +Base64.getEncoder().encodeToString(Pv2T));
                //判断Pv2T和pv2是否相等
                if (!strequalUnit.strequal(Pv2, Pv2T, DataUnit.HASH_LEN)) {
                    System.out.println("Recover error-2!");
                    return null;
                }

                //存储版本和对应的文件索引
                StoreUnit.RN.put(version, iDB);
                System.out.println("version的内容是:"+version);
                System.out.println("iDB的内容是:"+iDB);
                String strV = Base64.getEncoder().encodeToString(st2);  //strv即使表示st2的内容，对应版本的状态值

                StoreUnit.SR.put(astLen, strV);
                System.out.println("SR的strV的内容:"+Arrays.toString(Base64.getDecoder().decode(strV)));
                System.arraycopy(st1, 0, st2, 0, st1.length);
                System.out.println("st2的内容是"+Arrays.toString(st2));
                System.out.println("st2的加密内容是"+Base64.getEncoder().encodeToString(st2));

                System.out.println("pv1的内容"+Arrays.toString(Pv1));
                System.out.println("pv2的内容"+Arrays.toString(Pv2));
                if(Pv1.length<32){
                    System.arraycopy(Pv1, 0, Pv2, 0,Pv1.length);
                }else{
                    System.arraycopy(Pv1, 0, Pv2, 0, DataUnit.HASH_LEN);
                    System.out.println("pv1复制到pv2，这时Pv2的值时多少"+Base64.getEncoder().encodeToString(Pv2));
                }
                version++;
                astLen++;
                System.out.println("astLen的值:"+resultLen);
                resetUnit.reset(Pv2T, DataUnit.HASH_LEN);
            }
        }
        result[0] = resultLen;
        result[1] = astLen;
        result[2] = version;
        return result;
    }
}
